home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / mac / macmap.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-02  |  59.4 KB  |  2,145 lines  |  [TEXT/KAHL]

  1. /* Map graphics for the Mac interface to Xconq.
  2.    Copyright (C) 1992, 1993, 1994, 1995 Stanley T. Shebs.
  3.  
  4. Xconq is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.  See the file COPYING.  */
  8.  
  9. #include "conq.h"
  10. #include "macconq.h"
  11.  
  12. /* For these accessors, x must already be wrapped. */
  13.  
  14. #define cell_overlay(x, y)  \
  15.   (tmpdrawlighting  \
  16.    ? (night_at(x, y) ? -2 : (tmpdrawcoverage ? (cover(dside, x, y) == 0 ? -1 : 0) : 0))  \
  17.    : (tmpdrawcoverage ? (cover(dside, x, y) == 0 ? -1 : 0) : 0))
  18.  
  19. #define cell_terrain(x, y, power) (terrain_visible(x, y) ? terrain_at(x, y) : NONTTYPE)
  20.  
  21. #define cell_style(x, y, power)  \
  22.   ((terrain_visible(x, y) || unseen_image != NULL) ? (power >= 4 ? usepolygons : useblocks) : dontdraw);
  23.  
  24. #define SHIFT_ORIGIN(m) {  \
  25.   (m)->osx = (m)->vp->sx - (m)->conw;  (m)->osy = (m)->vp->sy - (m)->toph;  \
  26.   SetOrigin((m)->osx, (m)->osy);  \
  27.   }
  28.  
  29. #define RESET_ORIGIN(m) {  \
  30.   (m)->osx = 0;  (m)->osy = 0;  \
  31.   SetOrigin((m)->osx, (m)->osy);  \
  32.   }
  33.  
  34. static void draw_cliffs(Map *map, int x, int y);
  35.  
  36. int tmpdrawlighting;
  37.  
  38. int tmpdrawcoverage;
  39.  
  40. int seeall = FALSE;
  41.  
  42. int seeallterrain = FALSE;
  43.  
  44. int terrainseen = FALSE;
  45.  
  46. int mayseeall = FALSE;
  47.  
  48. int weseeall = FALSE;
  49.  
  50. /* The width of the left-side control panel. */
  51.  
  52. int conwid = 32;
  53.  
  54. /* The height of the top line. */
  55.  
  56. int tophgt = 16;
  57.  
  58. int mapnum = 1;
  59.  
  60. int nummaps = 0;
  61.  
  62. int lastmaph = -1, lastmapv = -1;
  63.  
  64. /* Handles of the pictures that display all the map control buttons. */
  65.  
  66. PicHandle tlcontrols = nil;
  67. PicHandle blcontrols = nil;
  68.  
  69. char *mouseover = "something";
  70.  
  71. int animation_pattern_state;
  72.  
  73. /* This tests whether the given cell might possibly be visible on the given map.
  74.    Not as precise as a real cliprect calculation. */
  75.  
  76. int
  77. at_all_visible(Map *map, int x, int y)
  78. {
  79.     return cell_is_visible(map->vp, x, y);
  80. }
  81.  
  82. /* Decide whether given location is away from the edge of the map's window. */
  83.  
  84. int
  85. in_middle(Map *map, int x, int y)
  86. {
  87.     return cell_is_in_middle(map->vp, x, y);
  88. }
  89.  
  90. /* Draw an individual detailed cell, as a row of one, on all maps. */
  91.  
  92. void  
  93. update_cell_display(Side *side, int x, int y, int rightnow)
  94. {
  95.     int i;
  96.     Unit *unit;
  97.     GrafPtr oldport;
  98.     Map *map;
  99.     RgnHandle tmprgn;
  100.     
  101.     if (active_display(side)) {
  102.         GetPort(&oldport);
  103.         for_all_maps(map) {
  104.             /* If update was only to report changes in undrawn info, maybe don't bother. */
  105.             if ((rightnow == 34 && !map->drawtemperature) 
  106.                 || (rightnow == 35 && !map->drawwinds)) continue;
  107.             if (at_all_visible(map, x, y)) {
  108.                 /* Set up the drawing context. */
  109.                 SetPort(map->window);
  110.                 tmprgn = NewRgn();
  111.                 GetClip(tmprgn);
  112.                 /* Clip to the content area of the map's window. */
  113.                 ClipRect(&(map->contentrect));
  114.                 SHIFT_ORIGIN(map);
  115.                 /* Draw the cell. */
  116.                 draw_row(map, x, y, 1, TRUE);
  117.                 /* Draw any selections that are here. */
  118. #if 1 /* (should eventually go away, when basic drawing does this right) */                
  119.                 for (i = 0; i < map->numselections; ++i) {
  120.                     unit = map->selections[i];
  121.                     if (unit && unit->x == x && unit->y == y) {
  122.                         draw_selected_unit(map, unit);
  123.                     }
  124.                 }
  125. #endif
  126.                 RESET_ORIGIN(map);
  127.                 SetClip(tmprgn);
  128.                 DisposeRgn(tmprgn);
  129.             }
  130.         }
  131.         SetPort(oldport);
  132.     }
  133. }
  134.  
  135. /* Create a new map window and all its paraphernalia. */
  136.  
  137. Map *
  138. create_map(int power)
  139. {
  140.     int m, t, i, x, y;
  141.     Rect vscrollrect, hscrollrect;
  142.     Map *map;
  143.     WindowPtr win;
  144.  
  145.     build_optional_terrain_type_menu();
  146.     DGprintf("Creating map, mag power %d\n", power);
  147.     map = (Map *) xmalloc(sizeof(Map));
  148.     map->vp = new_vp();
  149.     calc_vision();
  150.     set_map_power(map, power);
  151.     /* Pick an appropriate focus of the view. */
  152.     pick_a_focus(dside, &x, &y);
  153.     set_view_focus(map->vp, x, y);
  154.     /* Set default values for the display controls. */
  155.     map->conw = conwid;
  156.     map->toph = tophgt;
  157.     map->drawterrain = TRUE;
  158.     /* Display all types of borders and connections normally. */
  159.     /* (auxterraintypes[t] == 0 implies not optional type - i can't overflow
  160.        drawauxterrain because at least one ttype must be cell terrain.) */
  161.     /* (Addressing of map->auxterraintypes is 1-based instead of 0-based
  162.         because it corresponds to menu items.) */
  163.     i = 1;
  164.     for_all_terrain_types(t) {
  165.         if (!t_is_cell(t)) {
  166.             map->auxterraintypes[i++] = t;
  167.             map->drawauxterrain[t] = TRUE;
  168.         }
  169.     }
  170.     map->drawcellpats = TRUE;  /* should get from prefs */
  171.     map->drawunits = TRUE;
  172.     map->drawnames = default_draw_names;
  173.     map->drawpeople = FALSE;
  174.     map->drawelevations = FALSE;
  175.     for_all_material_types(m) {
  176.         map->drawmaterials[m] = FALSE;
  177.     }
  178.     map->nummaterialstodraw = 0;
  179.     map->drawlighting = TRUE;
  180.     map->drawtemperature = FALSE;
  181.     map->drawwinds = FALSE;
  182.     map->drawclouds = FALSE;
  183.     map->drawstorms = TRUE;
  184.     map->drawgrid = default_draw_grid;
  185.     /* Display AI info by default if there is an AI present. */
  186.     map->drawai = side_has_ai(dside);
  187.     /* Don't indicate other maps by default - too confusing initially. */
  188.     map->drawothermaps = FALSE;
  189.     map->autoselect = defaultautoselect;
  190.     map->moveonclick = defaultmoveonclick;
  191.     map->numselections = 0;
  192.     map->maxselections = max(100, numunits + numunits / 2);
  193.     map->selections = (Unit **) xmalloc(map->maxselections * sizeof(Unit *));
  194.     /* Newest map goes on the front of the list. */
  195.     map->next = maplist;
  196.     maplist = map;
  197.     if (hasColorQD) {
  198.         win = GetNewCWindow(wMap, nil, (WindowPtr) -1L);
  199.     } else {
  200.         win = GetNewWindow(wMap, nil, (WindowPtr) -1L);
  201.     }
  202.     map->window = win;
  203.     stagger_window(win, &lastmaph, &lastmapv);
  204.     if (first_windows) {
  205.         GDHandle main_screen = GetMainDevice();
  206.         int main_screen_width, main_screen_height;
  207.         Rect tmprect = win->portRect;
  208.         Rect gdrect = (*main_screen)->gdRect;
  209.         extern int gamewinw;
  210.         /* If the game involves construction, leave space to put up the
  211.            construction window. */
  212.         int constrheight = (any_construction_possible() ? 150 : 0);
  213.  
  214.         main_screen_width = gdrect.right - gdrect.left;
  215.         main_screen_height = gdrect.bottom - gdrect.top;
  216.         SizeWindow(win,
  217.                    main_screen_width - gamewinw - 10,
  218.                    main_screen_height - GetMBarHeight() - 16 - constrheight - 7,
  219.                    FALSE);
  220.     }
  221.     ShowWindow(win);
  222.     SetPort(win);
  223.     sprintf(spbuf, "Map %d", mapnum++);
  224.     add_window_menu_item(spbuf, win);
  225.     set_content_rect(map);
  226.     m_center_on_focus(map);
  227.     /* Make the scrollbars. */
  228.     vscrollrect = map->window->portRect;
  229.     vscrollrect.top -= 1;
  230.     vscrollrect.bottom -= sbarwid - 1;
  231.     vscrollrect.left = vscrollrect.right - sbarwid;
  232.     vscrollrect.right += 1;
  233.     map->vscrollbar =
  234.         NewControl(win, &vscrollrect, "\p", 1,
  235.              map->vp->sy, 0, max(0, map->vp->totsh - map->vp->pxh), scrollBarProc, 0L);
  236.     hscrollrect = win->portRect;
  237.     hscrollrect.top = hscrollrect.bottom - sbarwid;
  238.     hscrollrect.bottom += 1;
  239.     hscrollrect.left += map->conw;
  240.     hscrollrect.right -= sbarwid - 1;
  241.     map->hscrollbar =
  242.         NewControl(win, &hscrollrect, "\p", 1,
  243.              map->vp->sx, 0, max(0, map->vp->totsw - map->vp->pxw), scrollBarProc, 0L);
  244.     set_map_scrollbars(map);
  245.     ++nummaps;
  246.     return map;
  247. }
  248.  
  249. void
  250. calc_vision()
  251. {
  252.     /* (should be in kernel) */
  253.     seeall = g_see_all();
  254.     terrainseen = g_terrain_seen();
  255.     seeallterrain = all_terrain_visible();   /* (should tweak when vars change) */
  256. }
  257.  
  258. /* Compute the content part of the map window. */
  259.  
  260. void
  261. set_content_rect(Map *map)
  262. {
  263.     map->contentrect = map->window->portRect;
  264.     map->contentrect.left += map->conw;  map->contentrect.top += map->toph;
  265.     map->contentrect.right -= sbarwid;  map->contentrect.bottom -= sbarwid;
  266.     set_view_size(map->vp,
  267.                   map->contentrect.right - map->contentrect.left,
  268.                   map->contentrect.bottom - map->contentrect.top);
  269.     OffsetRect(&(map->contentrect), map->vp->sx - map->conw, map->vp->sy - map->toph);
  270. }
  271.  
  272. void
  273. m_focus_on_center(Map *map)
  274. {
  275.     focus_on_center(map->vp);
  276. }
  277.  
  278. /* Put vcx,vcy in the middle of the map. */
  279.  
  280. void
  281. m_center_on_focus(Map *map)
  282. {
  283.     center_on_focus(map->vp);
  284.     set_content_rect(map);
  285. }
  286.  
  287. /* Adjust the appearance and thumb of the scroll bars to reflect the map.  This is
  288.    needed whenever the map is scrolled under program control, such as when magnifying
  289.    or scrolling to a specified location. */
  290.  
  291. void
  292. set_map_scrollbars(Map *map)
  293. {
  294.     int sx, sy, hilite;
  295.     int hexadj = hexagon_adjust(map->vp);
  296.  
  297.     sx = map->vp->sx;  sy = map->vp->sy;
  298.     if (map->vp->pxw < (map->vp->totsw - hexagon_adjust(map->vp))) {
  299.         if (sx < hexadj)
  300.           sx = hexadj;
  301.         /* Set horiz min so that leftmost cell in area is at edge of window. */
  302.         SetCtlMin(map->hscrollbar, hexadj);
  303.         /* Wrapped-around areas need extra room to look at cells on the seam. */
  304.         SetCtlMax(map->hscrollbar, map->vp->totsw - (area.xwrap ? 0 : map->vp->pxw));
  305.         SetCtlValue(map->hscrollbar, sx);
  306.         hilite = TRUE;
  307.     } else {
  308.         /* Force sx to a default value and disable the scrollbar. */
  309.         sx = hexadj;
  310.         hilite = FALSE;
  311.     }
  312.     /* Adjust the hiliting of the scrollbar, but only if the window is in front,
  313.        otherwise the scrollbar should remain unhilited. */
  314.     if (map->window == FrontWindow()) {
  315.         HiliteControl(map->hscrollbar, (hilite ? 0 : 255));
  316.     }
  317.     DGprintf("Hscroll (%shilite) is %d -- %d -- %d\n",
  318.              (hilite ? "" : "no "), GetCtlMin(map->hscrollbar),
  319.              GetCtlValue(map->hscrollbar), GetCtlMax(map->hscrollbar));
  320.     if (map->vp->pxh < map->vp->totsh) {
  321.         /* Vertical scrollbar min is always zero. */
  322.         SetCtlMax(map->vscrollbar, map->vp->totsh - map->vp->pxh);
  323.         /* Constrain the scaled y position of the map.  This keeps the computed
  324.            y from exceeding what the scroll bar will allow (which happens because
  325.            the sy calcs don't take scroll bar limits into account, should be fixed). */
  326.         if (sy > map->vp->totsh - map->vp->pxh)
  327.           sy = map->vp->totsh - map->vp->pxh;
  328.         SetCtlValue(map->vscrollbar, sy);
  329.         hilite = TRUE;
  330.     } else {
  331.         /* Force sy to the top of the map and disable the scrollbar. */
  332.         sy = 0;
  333.         hilite = FALSE;
  334.     }
  335.     if (map->window == FrontWindow()) {
  336.         HiliteControl(map->vscrollbar, (hilite ? 0 : 255));
  337.     }
  338.     set_view_position(map->vp, sx, sy);
  339.     set_content_rect(map);
  340.     DGprintf("Vscroll (%shilite) is %d -- %d -- %d\n",
  341.              (hilite ? "" : "no "), GetCtlMin(map->vscrollbar),
  342.              GetCtlValue(map->vscrollbar), GetCtlMax(map->vscrollbar));
  343. }
  344.  
  345. /* Given a magnification power, look up and/or calculate the sizes of everything,
  346.    in pixels. */
  347.  
  348. void
  349. set_map_power(Map *map, int power)
  350. {
  351.     set_view_power(map->vp, power);
  352.     if (power >= 4 && cellrgns[power] == nil)
  353.       make_cell_clip(power);
  354. }
  355.  
  356. /* Given a magnification power, compute the clipping regions to be used at
  357.    that power. */
  358.  
  359. void
  360. make_cell_clip(int power)
  361. {
  362.     int hw = hws[power], hh = hhs[power], delt = (hhs[power] - hcs[power]);
  363.     PolyHandle poly;
  364.     RgnHandle tmprgn;
  365.  
  366.     /* Make a hexagon region by drawing a polygon and then framing it while
  367.        a region is open. */
  368.     poly = OpenPoly();
  369.     MoveTo(hw / 2, 0);
  370.     LineTo(hw, delt);
  371.     LineTo(hw, hh - delt);
  372.     LineTo(hw / 2, hh);
  373.     LineTo(0, hh - delt);
  374.     LineTo(0, delt);
  375.     LineTo(hw / 2, 0);
  376.     ClosePoly();
  377.     cellrgns[power] = NewRgn();
  378.     OpenRgn();
  379.     FramePoly(poly);
  380.     CloseRgn(cellrgns[power]);
  381.     /* Make the grid-displaying version of the hexagon. */
  382.     gridcellrgns[power] = NewRgn();
  383.     CopyRgn(cellrgns[power], gridcellrgns[power]);
  384.     /* Cut off a one-pixel line on the side. */
  385.     tmprgn = NewRgn();
  386.     SetRectRgn(tmprgn, hw - 1, 0, hw + 1, hh); 
  387.     DiffRgn(gridcellrgns[power], tmprgn, gridcellrgns[power]);
  388.     /* Now intersect with a region shifted upwards by one, which makes
  389.        the grid line along the bottom of the hex. */
  390.     tmprgn = NewRgn();
  391.     CopyRgn(cellrgns[power], tmprgn);
  392.     OffsetRgn(tmprgn, 0, -1);
  393.     SectRgn(gridcellrgns[power], tmprgn, gridcellrgns[power]);
  394.  
  395.     /* Similarly, but for cells at an angle. */
  396.     /* (should only calc when angle view first requested) */
  397.     /* First make a region as viewed at a 30-degree angle. */
  398.     hh /= 2;  delt /= 2;
  399.     poly = OpenPoly();
  400.     MoveTo(hw / 2, 0);
  401.     LineTo(hw, delt);
  402.     LineTo(hw, hh - delt);
  403.     LineTo(hw / 2, hh);
  404.     LineTo(0, hh - delt);
  405.     LineTo(0, delt);
  406.     LineTo(hw / 2, 0);
  407.     ClosePoly();
  408.     cellrgns30[power] = NewRgn();
  409.     OpenRgn();
  410.     FramePoly(poly);
  411.     CloseRgn(cellrgns30[power]);
  412.     gridcellrgns30[power] = NewRgn();
  413.     CopyRgn(cellrgns30[power], gridcellrgns30[power]);
  414.     /* Cut off a one-pixel line on the side. */
  415.     tmprgn = NewRgn();
  416.     SetRectRgn(tmprgn, hw - 1, 0, hw + 1, hh/2); 
  417.     DiffRgn(gridcellrgns30[power], tmprgn, gridcellrgns30[power]);
  418.     /* Now intersect with a region shifted upwards by one, which makes
  419.        the grid line along the bottom of the hex. */
  420.     tmprgn = NewRgn();
  421.     CopyRgn(cellrgns30[power], tmprgn);
  422.     OffsetRgn(tmprgn, 0, -1);
  423.     SectRgn(gridcellrgns30[power], tmprgn, gridcellrgns30[power]);
  424.  
  425.     /* Now make a region as viewed at a 15-degree angle. */
  426.     hh = hhs[power] / 4;
  427.     delt = (hhs[power] - hcs[power]) / 4;
  428.     poly = OpenPoly();
  429.     MoveTo(hw / 2, 0);
  430.     LineTo(hw, delt);
  431.     LineTo(hw, hh - delt);
  432.     LineTo(hw / 2, hh);
  433.     LineTo(0, hh - delt);
  434.     LineTo(0, delt);
  435.     LineTo(hw / 2, 0);
  436.     ClosePoly();
  437.     cellrgns15[power] = NewRgn();
  438.     OpenRgn();
  439.     FramePoly(poly);
  440.     CloseRgn(cellrgns15[power]);
  441.     gridcellrgns15[power] = NewRgn();
  442.     CopyRgn(cellrgns15[power], gridcellrgns15[power]);
  443.     /* Cut off a one-pixel line on the side. */
  444.     tmprgn = NewRgn();
  445.     SetRectRgn(tmprgn, hw - 1, 0, hw + 1, hh/2); 
  446.     DiffRgn(gridcellrgns15[power], tmprgn, gridcellrgns15[power]);
  447.     /* Now intersect with a region shifted upwards by one, which makes
  448.        the grid line along the bottom of the hex. */
  449.     tmprgn = NewRgn();
  450.     CopyRgn(cellrgns15[power], tmprgn);
  451.     OffsetRgn(tmprgn, 0, -1);
  452.     SectRgn(gridcellrgns15[power], tmprgn, gridcellrgns15[power]);
  453. }
  454.  
  455. /* Given a window, find the map that it belongs to. */
  456.  
  457. Map *
  458. map_from_window(WindowPtr window)
  459. {
  460.     Map *map;
  461.     
  462.     if (dside == NULL) return NULL;
  463.     for_all_maps(map) {
  464.         if (map->window == window) return map;
  465.     }
  466.     return NULL;
  467. }
  468.  
  469. /* Set the size of the map window and position its scrollbars correctly. */
  470.  
  471. void
  472. grow_map(Map *map, int w, int h)
  473. {
  474.     int oldsx = map->vp->sx, oldsy = map->vp->sy;
  475.     int oldpxw = map->vp->pxw, oldpxh = map->vp->pxh;
  476.     Rect tmprect;
  477.     WindowPtr mapwin = map->window;
  478.  
  479.     SizeWindow(map->window, w, h, FALSE);
  480.     adjust_map_decor(map);
  481.     set_content_rect(map);
  482.     set_map_scrollbars(map);
  483.     if (map->vp->sx == oldsx && map->vp->sy == oldsy) {
  484.         if (map->vp->pxh > oldpxh) {
  485.             SetRect(&tmprect, 0, oldpxh, map->vp->pxw, map->vp->pxh);
  486.             OffsetRect(&tmprect, map->conw, map->toph);
  487.             InvalRect(&tmprect);
  488.         }
  489.         if (map->vp->pxw > oldpxw) {
  490.             SetRect(&tmprect, oldpxw, 0, map->vp->pxw, map->vp->pxh);
  491.             OffsetRect(&tmprect, map->conw, map->toph);
  492.             InvalRect(&tmprect);
  493.         }
  494.     } else {
  495.         /* Be conservative and update everything. */
  496.         InvalRect(&mapwin->portRect);
  497.     }
  498.     /* Always need to update the controls. */
  499.     SetRect(&tmprect, 0, 0, map->conw, mapwin->portRect.bottom);
  500.     InvalRect(&tmprect);
  501.     /* Always need to update the topline. */
  502.     SetRect(&tmprect, 0, 0, mapwin->portRect.left, map->toph);
  503.     InvalRect(&tmprect);
  504.     draw_related_maps(map);
  505. }
  506.  
  507. /* Map zooming actually does "rightsizing" if possible. */
  508.  
  509. void
  510. zoom_map(Map *map, int part)
  511. {
  512.     WindowPtr mapwin = map->window;
  513.  
  514.     if (part == inZoomOut) {
  515.         set_standard_state(mapwin,
  516.                            area.width * map->vp->hw + map->conw + sbarwid + 1,
  517.                            area.height * map->vp->hch + (map->vp->hh - map->vp->hch) + sbarwid + map->toph + 1);
  518.     }
  519.     EraseRect(&mapwin->portRect);
  520.     ZoomWindow(mapwin, part, true);
  521.     adjust_map_decor(map);
  522.     set_content_rect(map);
  523.     /* (should try to minimize redraw here too) */
  524.     InvalRect(&mapwin->portRect);
  525. }
  526.  
  527. /* Move and size the controls to be correct for the map. */
  528.  
  529. void
  530. adjust_map_decor(Map *map)
  531. {                
  532.     int w, h;
  533.     WindowPtr mapwin = map->window;
  534.  
  535.     w = mapwin->portRect.right - mapwin->portRect.left;
  536.     h = mapwin->portRect.bottom - mapwin->portRect.top;
  537. /*    HideControl(map->hscrollbar); */
  538.     MoveControl(map->hscrollbar, map->conw - 1, h - sbarwid);
  539.     SizeControl(map->hscrollbar, w - map->conw - sbarwid + 2, sbarwid + 1);
  540. /*    HideControl(map->vscrollbar); */
  541.     MoveControl(map->vscrollbar, w - sbarwid, -1);
  542.     SizeControl(map->vscrollbar, sbarwid + 1, h - sbarwid + 1 + 1);
  543. }
  544.  
  545. /* Given a map and a cell, compute the pixel coords of the cell's UL corner.
  546.    This is the core routine that relates cells and pixels. */
  547.  
  548. void
  549. xform(map, x, y, sxp, syp)
  550. Map *map;
  551. int x, y, *sxp, *syp;
  552. {
  553.     xform_cell(map->vp, x, y, sxp, syp);
  554.     /* Shift the basic viewport result to account for both origin-shift and map decor. */
  555.     *sxp += map->osx + map->conw;  *syp += map->osy + map->toph;
  556. }
  557.  
  558. void
  559. m_xform_unit(map, unit, sxp, syp, swp, shp)
  560. Map *map;
  561. Unit *unit;
  562. int *sxp, *syp, *swp, *shp;
  563. {
  564.     xform_unit(map->vp, unit, sxp, syp, swp, shp);
  565.     /* Shift the basic viewport result to account for both origin-shift and map decor. */
  566.     *sxp += map->osx + map->conw;  *syp += map->osy + map->toph;
  567. }
  568.  
  569. void
  570. m_xform_unit_self(map, unit, sxp, syp, swp, shp)
  571. Map *map;
  572. Unit *unit;
  573. int *sxp, *syp, *swp, *shp;
  574. {
  575.     xform_unit_self(map->vp, unit, sxp, syp, swp, shp);
  576.     /* Shift the basic viewport result to account for both origin-shift and map decor. */
  577.     *sxp += map->osx + map->conw;  *syp += map->osy + map->toph;
  578. }
  579.  
  580. void
  581. m_xform_occupant(map, transport, unit, sx, sy, sw, sh, sxp, syp, swp, shp)
  582. Map *map;
  583. Unit *transport, *unit;
  584. int sx, sy, sw, sh, *sxp, *syp, *swp, *shp;
  585. {
  586.     /* Transform the coordinates back to relative values. */
  587.     sx -= map->osx + map->conw;  sy -= map->osy + map->toph;
  588.     xform_occupant(map->vp, transport, unit, sx, sy, sw, sh, sxp, syp, swp, shp);
  589.     /* Shift the basic viewport result to account for both origin-shift and map decor. */
  590.     *sxp += map->osx + map->conw;  *syp += map->osy + map->toph;
  591. }
  592.  
  593. /* Un-transform screen coordinates (as supplied by mouse perhaps) into
  594.    map coordinates.  */
  595.  
  596. /* Note that only drawing is affected by SetOrigin - mouse locations are
  597.    always window-relative, and thus only need to be adjusted by map decor
  598.    before going to the generic routines. */
  599.  
  600. int
  601. m_nearest_cell(Map *map, int sx, int sy, int *xp, int *yp)
  602. {
  603.     return nearest_cell(map->vp, sx - map->conw, sy - map->toph, xp, yp);
  604. }
  605.  
  606. /* Find the closest direction of the closest boundary.  */
  607.  
  608. int
  609. m_nearest_boundary(Map *map, int sx, int sy, int *xp, int *yp, int *dirp)
  610. {
  611.     return nearest_boundary(map->vp, sx - map->conw, sy - map->toph, xp, yp, dirp);
  612. }
  613.  
  614. int
  615. m_nearest_unit(Map *map, int sx, int sy, Unit **unitp)
  616. {
  617.     return nearest_unit(map->vp, sx - map->conw, sy - map->toph, unitp);
  618. }
  619.  
  620. /* Display a map and all of its paraphernalia. */
  621.  
  622. void
  623. draw_map(Map *map)
  624. {
  625.     Rect tmprect;
  626.     WindowPtr mapwin = map->window;
  627.     RgnHandle tmprgn;
  628.  
  629.     BackPat(QDPat(gray));
  630.     EraseRect(&(map->window->portRect));
  631.     /* Draw control panel and topline before clipping to inner part of window. */
  632.     if (map->conw > 0) draw_control_panel(map);
  633.     if (map->toph > 0) draw_top_line(map);
  634.     tmprgn = NewRgn();
  635.     GetClip(tmprgn);
  636.     ClipRect(&(map->contentrect));
  637.     SHIFT_ORIGIN(map);
  638.     draw_window_background(map);
  639.     RESET_ORIGIN(map);
  640.     /* Calculate shapes and sizes. */
  641.     set_map_scrollbars(map);
  642.     set_content_rect(map);
  643.     ClipRect(&(map->contentrect));
  644.     SHIFT_ORIGIN(map);
  645.     draw_area_background(map);
  646.     draw_map_content(map);
  647.     RESET_ORIGIN(map);
  648.     if (map->drawothermaps) draw_other_maps(map);
  649.     draw_selections(map);
  650. #ifdef DEBUGGING
  651.     /* Indicate where the focus is. */
  652.     if (DebugG) {
  653.         int sx, sy;
  654.  
  655.         ClipRect(&(map->contentrect));
  656.         SHIFT_ORIGIN(map);
  657.         xform(map, map->vp->vcx, map->vp->vcy, &sx, &sy);
  658.         SetRect(&tmprect, sx, sy, sx + map->vp->hw, sy + map->vp->hh);
  659.         InsetRect(&tmprect, -4, -4);
  660.         InvertOval(&tmprect);
  661.         InsetRect(&tmprect, 2, 2);
  662.         InvertOval(&tmprect);
  663.         RESET_ORIGIN(map);
  664.     }
  665. #endif /* DEBUGGING */
  666.     SetClip(tmprgn);
  667.     DisposeRgn(tmprgn);
  668. }
  669.  
  670. void
  671. draw_window_background(Map *map)
  672. {
  673.     /* If part of the window is entirely outside the world, we draw its shape on
  674.        top of gray, otherwise window starts out all white. */
  675.     if (area.width * map->vp->hw < 32000) {
  676.         switch (bggray) {
  677.             case blackgray:
  678.                 FillRect(&(map->contentrect), QDPat(black));  break;
  679.             case darkgray:
  680.                 FillRect(&(map->contentrect), QDPat(dkGray));  break;
  681.             case mediumgray:
  682.                 FillRect(&(map->contentrect), QDPat(gray));  break;
  683.             case lightgray:
  684.                 FillRect(&(map->contentrect), QDPat(ltGray));  break;
  685.             case whitegray:
  686.                 FillRect(&(map->contentrect), QDPat(white));  break;
  687.         }
  688.     } else {
  689.         if (hasColorQD) {
  690.             RGBForeColor((grid_matches_unseen ? &gridcolor : &unseencolor));
  691.             PaintRect(&(map->contentrect));
  692.             RGBForeColor(&blackcolor);
  693.         } else {
  694.             switch ((grid_matches_unseen ? gridgray : unseengray)) {
  695.                 case blackgray:
  696.                     FillRect(&(map->contentrect), QDPat(black));  break;
  697.                 case darkgray:
  698.                     FillRect(&(map->contentrect), QDPat(dkGray));  break;
  699.                 case mediumgray:
  700.                     FillRect(&(map->contentrect), QDPat(gray));  break;
  701.                 case lightgray:
  702.                     FillRect(&(map->contentrect), QDPat(ltGray));  break;
  703.                 case whitegray:
  704.                     FillRect(&(map->contentrect), QDPat(white));  break;
  705.             }
  706.         }
  707.     }
  708. }
  709.  
  710. /* Draw the actual map data.  This is basically a matter of drawing n rows of terrain,
  711.    each of an appropriate length, but it's a bit of trouble to get the number and
  712.    lengths right.  Actually, it's easy to get approximate sizes, but it's important
  713.    to try to draw as few cells as humanly possible. */
  714.  
  715. void
  716. draw_map_content(map)
  717. Map *map;
  718. {
  719.     int y1, y2, y, x1, x2, xx1, yy1, xx2, yy2;
  720.     int vx, vy, vw, vh;
  721.     int halfheight = area.height / 2;
  722.     int limitleft = FALSE, limitrite = FALSE;
  723.     Rect bbox = (*(map->window->visRgn))->rgnBBox;
  724.     Rect tmprect = map->contentrect;
  725.  
  726.     if (DebugG) {
  727.         FillRgn(map->window->visRgn, QDPat(white));
  728.     }
  729.     /* Compute the size of the viewport.  Make sure it will extend past the edges
  730.        of the window, so that partial cells around the edges will be filled in. */
  731.     vw = min(area.width, map->vp->pxw / map->vp->hw + 2);
  732.     vh = min(area.height, map->vp->pxh / map->vp->hch + 2);
  733.     /* Compute the bottom visible row. */
  734.     vy = ((map->vp->totsh - map->vp->sy) / map->vp->hch) - vh;
  735.     /* Now adjust the bottom row so it doesn't go outside the area. */
  736.     if (vy < 0)
  737.       vy = 0;
  738.     if (vy > area.height - vh)
  739.       vy = area.height - vh;
  740.     /* Compute the leftmost "column". */
  741.     vx = map->vp->sx / map->vp->hw - vy / 2 - 1;
  742.     DGprintf("Set %dx%d viewport at %d,%d\n", vw, vh, vx, vy);
  743.     /* Compute top and bottom rows to be displayed. */
  744.     y1 = min(vy + vh, area.height - 1);
  745.     y2 = vy;
  746.     /* Find the top and bottom rows that are in the visRgn. */
  747.     OffsetRect(&bbox, - map->vp->sx, - map->vp->sy);
  748.     if (m_nearest_cell(map, bbox.left, bbox.top, &xx1, &yy1))
  749.       limitleft = TRUE;
  750.     if (m_nearest_cell(map, bbox.right + map->vp->hw, bbox.bottom + map->vp->hh, &xx2, &yy2))
  751.       limitrite = TRUE;
  752.     xx2 += 5;
  753.     /* This fixes problem of missing rows - wish I knew why it worked :-( */
  754.     yy2 -= 3;
  755.     DGprintf("Map rows are %d - %d, update area rows are %d - %d\n", y2, y1, yy2, yy1);
  756.     if (between(y2, yy1, y1))
  757.       y1 = yy1; 
  758.     if (between(y2, yy2, y1))
  759.       y2 = yy2;
  760.     /* Draw the rows, going from top to bottom. */
  761.     for (y = y1; y >= y2; --y) {
  762.         /* Adjust the right and left bounds to fill the viewport as
  763.            much as possible, without going too far (the drawing code
  764.            will clip, but clipped drawing is still expensive). */
  765.         /* could test by drawing viewport rect as lines... */
  766.         x1 = vx - (y - vy) / 2;
  767.         x2 = x1 + vw + 2;
  768.         /* If the area doesn't wrap, then we might have to stop
  769.            drawing before we reach the edge of the viewport. */
  770.         if (area.xwrap) {
  771.             /* (should clip to visrgn, but tricky to avoid wrapping bugs) */
  772.         } else {
  773.             /* Truncate x's to stay within the area. */
  774.             x1 = max(0, min(x1, area.width-1));
  775.             x2 = max(0, min(x2, area.width));
  776.             /* If this row is entirely in the NE corner, don't draw anything. */
  777.             if (x1 + y > area.width + halfheight)
  778.               continue;
  779.             /* If this row is entirely in the SW corner, don't draw anything. */
  780.             if (x2 + y < halfheight)
  781.               continue;
  782.             /* If the row ends up in the NE corner, shorten it. */
  783.             if (x2 + y > area.width + halfheight)
  784.               x2 = area.width + halfheight - y;
  785.             /* If the row starts out in the SW corner, shorten it. */
  786.             if (x1 + y < halfheight)
  787.               x1 = halfheight - y;
  788.             /* Clip the ends of the row to the visRgn. */
  789.             if (limitleft && between(x1, xx1, x2))
  790.               x1 = xx1; 
  791.             if (limitrite && between(x1, xx2, x2))
  792.               x2 = xx2;
  793.         }
  794.         draw_row(map, x1, y, x2 - x1, FALSE);
  795.     }
  796. }
  797.  
  798. /* This draws a hexagon or rectangle that covers the totality of the area, whether
  799.    discovered or not. */
  800.  
  801. void 
  802. draw_area_background(Map *map)
  803. {
  804.     int sx, sy, llx, lly, lrx, lry, rx, ry, urx, ury, ulx, uly, lx, ly;
  805.     PolyHandle poly;
  806.     Rect arearect;
  807.  
  808.     /* Don't bother if area magnified greatly. */
  809.     /* (should fix to not try to draw giant rects, but still draw something reasonable.
  810.        note that otherwise grid color may be wrong) */
  811.     if (area.width * map->vp->hw > 32000)
  812.       return;
  813.     if (area.xwrap) {
  814.         /* Area is cylinder; draw a rectangle. */
  815.         xform(map, 0, area.height-1, &sx, &sy);
  816.         arearect.left = 0;  arearect.top = sy;
  817.         xform(map, 0, 0, &sx, &sy);
  818.         arearect.right = area.width * map->vp->hw;  arearect.bottom = sy;
  819.         /* Adjust so that edges of the rect pass through the middles of edge cells. */
  820.         OffsetRect(&arearect, map->vp->hw/2, map->vp->hh/2);
  821.         if (hasColorQD) {
  822.             RGBForeColor(&gridcolor);
  823.             PaintRect(&arearect);
  824.             RGBForeColor(&blackcolor);
  825.         } else {
  826.             switch (gridgray) {
  827.                 case blackgray:
  828.                     FillRect(&arearect, QDPat(black));   break;
  829.                 case darkgray:
  830.                     FillRect(&arearect, QDPat(dkGray));  break;
  831.                 case mediumgray:
  832.                     FillRect(&arearect, QDPat(gray));    break;
  833.                 case lightgray:
  834.                     FillRect(&arearect, QDPat(ltGray));  break;
  835.                 case whitegray:
  836.                     FillRect(&arearect, QDPat(white));   break;
  837.             }
  838.         }
  839.     } else {
  840.         /* Area is hexagon; draw a hexagon. */
  841.         /* (should make once and save?) */
  842.         poly = OpenPoly();        
  843.         xform(map, 0 + area.height/2, 0, &llx, &lly);
  844.         MoveTo(llx, lly);
  845.         xform(map, area.width-1, 0, &lrx, &lry);
  846.          LineTo(lrx, lry);
  847.         xform(map, area.width-1, area.height/2, &rx, &ry);
  848.         LineTo(rx, ry);
  849.          xform(map, area.width-1 - area.height/2, area.height-1, &urx, &ury);
  850.         LineTo(urx, ury);
  851.          xform(map, 0, area.height-1, &ulx, &uly);
  852.         LineTo(ulx, uly);
  853.          xform(map, 0, area.height/2, &lx, &ly);
  854.         LineTo(lx, ly);
  855.         LineTo(llx, lly);
  856.         ClosePoly();
  857.         /* Adjust so that edges of the polygon pass through the middles of edge cells. */
  858.         OffsetPoly(poly, map->vp->hw/2, map->vp->hh/2);
  859.         if (hasColorQD) {
  860.             RGBForeColor(&gridcolor);
  861.             PaintPoly(poly);
  862.             RGBForeColor(&blackcolor);
  863.         } else {
  864.             switch (gridgray) {
  865.                 case blackgray:
  866.                     FillPoly(poly, QDPat(black));   break;
  867.                 case darkgray:
  868.                     FillPoly(poly, QDPat(dkGray));  break;
  869.                 case mediumgray:
  870.                     FillPoly(poly, QDPat(gray));    break;
  871.                 case lightgray:
  872.                     FillPoly(poly, QDPat(ltGray));  break;
  873.                 case whitegray:
  874.                     FillPoly(poly, QDPat(white));   break;
  875.             }
  876.         }
  877.         /* Free up the space for this hexagon. */
  878.         KillPoly(poly);
  879.     }
  880. #if 0  /* The idea of a shaded border seems nice, but it doesn't look very good in practice. */
  881.     for (x = 0; x < area.width; ++x) {
  882.         xform(map, x, 0, &sx, &sy);
  883.         draw_border_line(sx, sy, SW, map->vp->power, -1);
  884.         draw_border_line(sx, sy, SE, map->vp->power, -2);
  885.     }
  886.     PenNormal();
  887. #endif
  888. }
  889.  
  890. /* Draw the map control panel as a pair of PICTs. */
  891.  
  892. void
  893. draw_control_panel(map)
  894. Map *map;
  895. {
  896.     int winhgt, basev, h;
  897.     Rect tmprect;
  898.  
  899.     winhgt = (map->window->portRect).bottom - (map->window->portRect).top;
  900.     SetRect(&tmprect, 0, 0, map->conw, winhgt);
  901.     FillRect(&tmprect, QDPat(white));
  902.     MoveTo(map->conw - 1, 0);  Line(0, winhgt);
  903.     if (tlcontrols == nil) {
  904.         tlcontrols = (PicHandle) GetResource('PICT', pMapControlsTL);
  905.     }
  906.     if (tlcontrols != nil) {
  907.         SetRect(&tmprect, 0, 0,
  908.                 picture_width(tlcontrols), picture_height(tlcontrols));
  909.         DrawPicture(tlcontrols, &tmprect);
  910.     }
  911.     if (blcontrols == nil) {
  912.         blcontrols = (PicHandle) GetResource('PICT', pMapControlsBL);
  913.     }
  914.     if (blcontrols != nil) {
  915.         SetRect(&tmprect, 0, winhgt - picture_height(blcontrols) + 1,
  916.                 picture_width(blcontrols), winhgt);
  917.         DrawPicture(blcontrols, &tmprect);
  918.     }
  919.     if (map->moveonclick && map->autoselect) {
  920.         SetRect(&tmprect, 4, 5, 26, 26);
  921.         InvertRect(&tmprect);
  922.     }
  923.     /* (should modify appearance of top left arrow buttons to reflect abilities) */
  924.     basev = 32 + 5*15 + 2 + 5/*why?*/ + 1;
  925.     SetRect(&tmprect, 0, basev, 30, basev + 10);
  926.     if (map->drawgrid) {
  927.         InvertRect(&tmprect);
  928.     }
  929.     OffsetRect(&tmprect, 0, 11);
  930.     if (map->drawnames) {
  931.         InvertRect(&tmprect);
  932.     }
  933.     OffsetRect(&tmprect, 0, 11);
  934.     if (map->drawpeople) {
  935.         InvertRect(&tmprect);
  936.     } else if (!people_sides_defined()) {
  937.         gray_out_rect(&tmprect);
  938.     }
  939.     OffsetRect(&tmprect, 0, 11);
  940.     if (map->drawplans) {
  941.         InvertRect(&tmprect);
  942.     }
  943.     OffsetRect(&tmprect, 0, 11);
  944.     if (map->drawai) {
  945.         InvertRect(&tmprect);
  946.     } else if (!side_has_ai(dside)) {
  947.         /* (should ensure that this is updated when side gets an AI) */
  948.         gray_out_rect(&tmprect);
  949.     }
  950.     /* If the game is over, draw a button that shows us everything if clicked. */
  951.     if (endofgame
  952.         && (winhgt - (picture_height(tlcontrols) + picture_height(blcontrols))) > 20) {
  953.         h = picture_height(tlcontrols)
  954.             + (winhgt - (picture_height(tlcontrols) + picture_height(blcontrols))) / 2
  955.             - 3;
  956.         SetRect(&tmprect, 2, h, 28, h + 14);
  957.         if (weseeall) {    
  958.             InvertRect(&tmprect);
  959.         } else {
  960.             FrameRect(&tmprect);
  961.         }
  962.     }
  963.     /* Draw state of bottom left control buttons. */
  964.     if (map->vp->power <= 0) {
  965.         SetRect(&tmprect, 0, winhgt - 15, 15, winhgt);
  966.         gray_out_rect(&tmprect);
  967.     }
  968.     if (map->vp->power >= NUMPOWERS - 1) {
  969.         SetRect(&tmprect, 16, winhgt - 15, 30, winhgt);
  970.         gray_out_rect(&tmprect);
  971.     }
  972. }
  973.  
  974. void
  975. draw_top_line(Map *map)
  976. {
  977.     int numchars, strwid;
  978.     Rect tmprect;
  979.  
  980.     /* Clear the whole topline area. */
  981.     SetRect(&tmprect, map->conw, 0, map->conw + map->vp->pxw, tophgt);
  982.     FillRect(&tmprect, QDPat(white));
  983.     /* Draw a line dividing this from the map content. */
  984.     MoveTo(map->conw, tmprect.bottom - 1);
  985.     Line(map->vp->pxw, 0);
  986.     /* Draw the current date. */
  987.     TextSize(10);
  988.     strwid = TextWidth(curdatestr, 1, curdatestr[0]);
  989.     MoveTo(tmprect.right - strwid - 3, tmprect.top + 11);
  990.     numchars = curdatestr[0];
  991.     DrawText(curdatestr, 1, numchars);
  992.     if (mouseover != NULL) {
  993.         /* Draw description of what the mouse is over. */
  994.         /* (should clip to avail space) */
  995.         numchars = strlen(mouseover);
  996.         MoveTo(map->conw + 10, tmprect.top + 11);
  997.         DrawText(mouseover, 0, numchars);
  998.     }
  999. }
  1000.  
  1001. /* Draw an indication of the position of other maps relative to this one. */
  1002.  
  1003. void
  1004. draw_other_maps(map)
  1005. Map *map;
  1006. {
  1007.     Map *map2;
  1008.  
  1009.     for_all_maps(map2) {
  1010.         if (map != map2 /* && reasonable to show? */) {
  1011.             draw_other_map(map, map2);
  1012.         }
  1013.     }
  1014. }
  1015.  
  1016. void
  1017. draw_related_maps(map)
  1018. Map *map;
  1019. {
  1020.     Map *map2;
  1021.     GrafPtr oldport;
  1022.  
  1023.     for_all_maps(map2) {
  1024.         if (map != map2 && map2->drawothermaps /* && reasonable to show? */) {
  1025.             GetPort(&oldport);
  1026.             SetPort(map2->window);
  1027.             /* (also clipping?) */
  1028.             draw_other_map(map2, map);
  1029.             SetPort(oldport);
  1030.         }
  1031.     }
  1032. }
  1033.  
  1034. void
  1035. draw_other_map(map, map2)
  1036. Map *map, *map2;
  1037. {
  1038.     int sx, sy, sw, sh;
  1039.     Rect tmprect;
  1040.  
  1041.     scale_vp(map->vp, map2->vp, &sx, &sy, &sw, &sh);
  1042.     SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
  1043.     OffsetRect(&tmprect, map->conw, map->toph);
  1044.     if (map->vp->hw < 8) PenSize(2, 2);
  1045.     PenMode(patXor);
  1046.     FrameRect(&tmprect);
  1047.     PenNormal();
  1048. }
  1049.  
  1050. /* x0 may be negative here. */
  1051.  
  1052. /* (should add a "draw interior only" version of this routine?) */
  1053.  
  1054. void
  1055. draw_row(map, x0, y0, len, clearit)
  1056. Map *map;
  1057. int x0, y0, len, clearit;
  1058. {
  1059.     int empty = FALSE;
  1060.     int i = 0, x, xw, sx, sy, t;
  1061.  
  1062.     if (!between(0, y0, area.height - 1))
  1063.       return;
  1064.     if (partial_views() && !map->drawai && unseen_image == NULL) {
  1065.         empty = TRUE;
  1066.         /* Examine row to see if we can skip it entirely. */
  1067.         for (x = x0; x < x0 + len; ++x) {
  1068.             xw = wrapx(x);
  1069.             if (terrain_visible(xw, y0)
  1070.                 || (numbordtypes > 0 && terrain_visible(xw, y0 + 1))
  1071.                 ) {
  1072.                 empty = FALSE;
  1073.                 break;
  1074.             }
  1075.         }
  1076.     }
  1077.     if (empty && !clearit)
  1078.       return;
  1079.     /* The terrain always comes first. */
  1080.     if (map->drawterrain) {
  1081.         draw_terrain_row(map, x0, y0, len);
  1082.         /* Maybe draw cliffs. */
  1083.         if (elevations_defined() && map->vp->angle != 90 && y0 > 0) {
  1084.             for (x = x0; x < x0 + len; ++x) {
  1085.                 if (terrain_visible(x, y0)) {
  1086.                     draw_cliffs(map, x, y0);
  1087.                 }
  1088.             }
  1089.         }
  1090.         if (any_aux_terrain_defined()) {
  1091.             /* The relative ordering of these is quite important - connections
  1092.                should always be drawn on top of borders. */
  1093.             if (bords_to_draw(map)) {
  1094.                 for_all_terrain_types(t) {
  1095.                     if (map->drawauxterrain[t] && t_is_border(t) && aux_terrain_defined(t)) {
  1096.                         for (x = x0; x < x0 + len; ++x) {
  1097.                             draw_borders(map, x, y0, t);
  1098.                         }
  1099.                     }
  1100.                 }
  1101.             }
  1102.             if (conns_to_draw(map)) {
  1103.                 for_all_terrain_types(t) {
  1104.                     if (map->drawauxterrain[t] && t_is_connection(t) && aux_terrain_defined(t)) {
  1105.                         for (x = x0; x < x0 + len; ++x) {
  1106.                             draw_connections(map, x, y0, t);
  1107.                         }
  1108.                     }
  1109.                 }
  1110.             }
  1111.         }
  1112.     }
  1113.     /* Although we had to draw the terrain on the edge, we can skip everything else,
  1114.        since edge cells have no units, weather, etc. */
  1115.     /* Skip the top and bottom edge rows. */
  1116.     if (!between(1, y0, area.height - 2)) 
  1117.       return;
  1118.     /* Shorten the row by one cell on each side, if those are edge cells. */
  1119.     if (!inside_area(x0+len-1, y0))
  1120.       --len;
  1121.     if (len <= 0)
  1122.       return;
  1123.     if (!inside_area(x0, y0))
  1124.       ++x0;
  1125.     if (any_cell_materials_defined() && map->nummaterialstodraw > 0 && map->vp->hh > 20) {
  1126.         for (x = x0; x < x0 + len; ++x) {
  1127.             draw_materials(map, x, y0);
  1128.         }
  1129.     }
  1130.     /* (should be global to entire map drawing somehow?) */
  1131.     if (map->drawnames && map->vp->hh > 5) {
  1132.         for (x = x0; x < x0 + len; ++x) {
  1133.             draw_legend(map, x, y0);
  1134.         }
  1135.     }
  1136.     if (people_sides_defined() && bwid2[map->vp->power] > 0) {
  1137.         draw_people_row(map, x0, y0, len);
  1138.     }
  1139.     if (map->drawunits && map->vp->hw > 2) {
  1140.         for (x = x0; x < x0 + len; ++x) {
  1141.             draw_units(map, x, y0);
  1142.         }
  1143.     }
  1144.     if (map->drawai) {
  1145.         for (x = x0; x < x0 + len; ++x) {
  1146.             draw_theater(map, x, y0);
  1147.         }
  1148.     }
  1149.     /* If debugging, draw coverage on top of everything else. */
  1150.     if (DebugG && !g_see_all()) {
  1151.         for (x = x0; x < x0 + len; ++x) {
  1152.             xform(map, x, y0, &sx, &sy);
  1153.             draw_coverage(sx, sy, map->vp->power, cover(dside, x, y0));
  1154.         }
  1155.     }
  1156. }
  1157.  
  1158. /* Is this just to see if a cell is visible? */
  1159.  
  1160. int
  1161. cell_update(map, x, y)
  1162. Map *map;
  1163. int x, y;
  1164. {
  1165. #if 0
  1166.     int sx, sy;
  1167.     Rect tmprect;
  1168.  
  1169.     xform(map, x, y, &sx, &sy);
  1170.     SetRect(&tmprect, sx, sy, sx + map->vp->hw, sy + map->vp->hh);
  1171.     return (RectInRgn(&tmprect, map->window->visRgn));
  1172. #else
  1173.     return TRUE;
  1174. #endif
  1175. }
  1176.  
  1177. /* Draw an entire row of terrain, possibly with a single rectangle fill. */
  1178.  
  1179. /* x0 may be negative. */
  1180.  
  1181. extern int sunx, suny;
  1182.  
  1183. void
  1184. draw_terrain_row(map, x0, y0, len)
  1185. Map *map;
  1186. int x0, y0, len;
  1187. {
  1188.     int x0w, x, xw, x1, x1w, sx, sy, i;
  1189.     int pwr = map->vp->power, ang = map->vp->angle;
  1190.     int dogrid = map->drawgrid, dofill = map->drawcellpats;
  1191.     int inarea, seginarea, style, segstyle, terr, segterr, over, segover, update, segupdate;
  1192.  
  1193.     tmpdrawlighting = map->drawlighting;
  1194.     tmpdrawcoverage = (!g_see_all() && map->drawcover);
  1195.     x0w = wrapx(x0);
  1196.     i = 0;
  1197.     x1 = x0;
  1198.     x1w = wrapx(x1);
  1199.     seginarea = in_area(x0w, y0);
  1200.     segstyle = cell_style(x0w, y0, pwr);
  1201.     segterr = cell_terrain(x0w, y0, pwr);
  1202.     segover = cell_overlay(x0w, y0);
  1203.     segupdate = (seginarea ? cell_update(map, x0w, y0) : FALSE);
  1204.     for (x = x0; x < x0 + len + 1; ++x) {
  1205.         xw = wrapx(x);
  1206.         inarea = in_area(xw, y0);
  1207.         style = cell_style(xw, y0, pwr);
  1208.         terr = cell_terrain(xw, y0, pwr);
  1209.         over = cell_overlay(xw, y0);
  1210.         update = (inarea ? cell_update(map, xw, y0) : FALSE);
  1211.         /* Decide if the run is over and we need to dump some output. */
  1212.         if (x == x0 + len
  1213.             || inarea != seginarea
  1214.             || style != segstyle
  1215.             || terr != segterr
  1216.             || over != segover
  1217.             || update != segupdate
  1218. /*            || segstyle == useblocks  fixes, but poor performance */
  1219.             || segstyle == usepictures
  1220.             || segstyle == usepolygons
  1221.             || ang != 90) {
  1222.             /* don't draw anything that would match the window's bg */
  1223. #if 0 /* use this for heavy-duty debugging only */
  1224.             DGprintf("Seg is %d,%d len=%d inarea=%d style=%d terr=%d over=%d update=%d\n",
  1225.                      x1, y0, i, seginarea, segstyle, segterr, segover, segupdate);
  1226. #endif
  1227.             if (seginarea && segupdate && segstyle != dontdraw) {
  1228.                 xform(map, x1, y0, &sx, &sy);
  1229.                 if (area.xwrap && sx > map->vp->totsw)
  1230.                   sx -= map->vp->totsw;
  1231.                 switch (segstyle) {
  1232.                     case useblocks:
  1233.                         draw_cell_block(sx, sy, i, pwr, segterr, segover, ang);
  1234.                         break;
  1235.                     case usepictures:
  1236.                         break;
  1237.                     case usepolygons:
  1238.                         draw_hex_region(sx, sy, pwr, map->drawgrid, segterr, segover, ang);
  1239.                         /* Assume that only polygon scale can fit numbers. */
  1240.                         if (elevations_defined()
  1241.                             && map->drawelevations
  1242.                             && draw_elevation_here(x1w, y0)) {
  1243.                             draw_elevation(sx, sy, pwr, elev_at(x1w, y0));
  1244.                         }
  1245.                         if (clouds_defined()
  1246.                             && map->drawclouds
  1247.                             && draw_clouds_here(x1w, y0)) {
  1248.                             draw_clouds(sx, sy, pwr, raw_cloud_at(x1w, y0));
  1249.                         }
  1250.                         if (winds_defined()
  1251.                             && map->drawwinds
  1252.                             && draw_winds_here(x1w, y0)) {
  1253.                             draw_winds(sx, sy, pwr, wind_dir_at(x1w, y0), wind_force_at(x1w, y0));
  1254.                         }
  1255.                         if (temperatures_defined()
  1256.                             && map->drawtemperature
  1257.                             && draw_temperature_here(x1w, y0)) {
  1258.                             draw_temperature(sx, sy, pwr, temperature_at(x1w, y0));
  1259.                         }
  1260.                 }
  1261.             }
  1262.             /* Set up for the next segment. */
  1263.             i = 0;
  1264.             x1 = x;
  1265.             x1w = wrapx(x1);
  1266.             seginarea = inarea;
  1267.             segstyle = style;
  1268.             segterr = terr;
  1269.             segover = over;
  1270.             segupdate = update;
  1271.         }
  1272.         ++i;
  1273.     }
  1274. }
  1275.  
  1276. static void
  1277. draw_cliffs(Map *map, int x, int y)
  1278. {
  1279.     int elev, x1, y1, drop, t;
  1280.     int sx, sy, sx1, sy1;
  1281.     PolyHandle poly;
  1282.     RGBColor hexcolor, oldcolor;
  1283.  
  1284.     elev = elev_at(x, y) + t_thickness(terrain_at(x, y));
  1285.     if (point_in_dir(x, y, SOUTHWEST, &x1, &y1)) {
  1286.         drop = elev - (elev_at(x1, y1) + t_thickness(terrain_at(x1, y1)));
  1287.         if (drop > 0) {
  1288.             xform(map, x, y, &sx, &sy);
  1289.             xform(map, x1, y1, &sx1, &sy1);
  1290.             if (sy1 > sy + map->vp->hch + (map->drawgrid ? 1 : 0)) {
  1291.                 poly = OpenPoly();
  1292.                 MoveTo(sx, sy + map->vp->hch);
  1293.                 LineTo(sx + map->vp->hw / 2 - (map->drawgrid ? 1 : 0), sy + map->vp->hh);
  1294.                 LineTo(sx + map->vp->hw / 2 - (map->drawgrid ? 1 : 0), sy1 + (map->vp->hh - map->vp->hch));
  1295.                 LineTo(sx, sy1);
  1296.                 LineTo(sx, sy + map->vp->hch);
  1297.                 ClosePoly();
  1298.                 t = terrain_at(x, y);
  1299.                 /* if (timg->colrpat != nil
  1300.                     && (minscreendepth > 1 || !timg->patdefined)) {
  1301.                     FillCPoly(poly, timg->colrpat);
  1302.                 } else */ if (tcolors[t] != NULL && tcolors[t]->defined && maxscreendepth > 1) {
  1303.                     hexcolor.red   = (tcolors[t]->r / 2) << 8;
  1304.                     hexcolor.green = (tcolors[t]->g / 2) << 8;
  1305.                     hexcolor.blue  = (tcolors[t]->b / 2) << 8;
  1306.                     RGBForeColor(&hexcolor);
  1307.                     PaintPoly(poly);
  1308.                     /* Restore the previous color. */
  1309.                     oldcolor.red = oldcolor.green = oldcolor.blue = 0;
  1310.                     RGBForeColor(&oldcolor);
  1311.                 } else {
  1312.                     hexcolor.red   = 128 << 8;
  1313.                     hexcolor.green = 128 << 8;
  1314.                     hexcolor.blue  = 128 << 8;
  1315.                     RGBForeColor(&hexcolor);
  1316.                     PaintPoly(poly);
  1317.                     /* Restore the previous color. */
  1318.                     oldcolor.red = oldcolor.green = oldcolor.blue = 0;
  1319.                     RGBForeColor(&oldcolor);
  1320.                 }
  1321.                 KillPoly(poly);
  1322.             }
  1323.         }
  1324.     }
  1325.     if (point_in_dir(x, y, SOUTHEAST, &x1, &y1)) {
  1326.         drop = elev - (elev_at(x1, y1) + t_thickness(terrain_at(x1, y1)));
  1327.         if (drop > 0) {
  1328.             xform(map, x, y, &sx, &sy);
  1329.             xform(map, x1, y1, &sx1, &sy1);
  1330.             if (sy1 > sy + map->vp->hch + (map->drawgrid ? 1 : 0)) {
  1331.                 poly = OpenPoly();
  1332.                 MoveTo(sx + map->vp->hw - (map->drawgrid ? 1 : 0),     sy + map->vp->hch);
  1333.                 LineTo(sx + map->vp->hw - (map->drawgrid ? 1 : 0),     sy1);
  1334.                 LineTo(sx + map->vp->hw / 2, sy1 + (map->vp->hh - map->vp->hch));
  1335.                 LineTo(sx + map->vp->hw / 2, sy + map->vp->hh);
  1336.                 LineTo(sx + map->vp->hw - (map->drawgrid ? 1 : 0),     sy + map->vp->hch);
  1337.                 ClosePoly();
  1338.                 t = terrain_at(x, y);
  1339.                 /* if (timg->colrpat != nil
  1340.                     && (minscreendepth > 1 || !timg->patdefined)) {
  1341.                     FillCPoly(poly, timg->colrpat);
  1342.                 } else */ if (tcolors[t] != NULL && tcolors[t]->defined && maxscreendepth > 1) {
  1343.                     hexcolor.red   = (tcolors[t]->r) << 8;
  1344.                     hexcolor.green = (tcolors[t]->g) << 8;
  1345.                     hexcolor.blue  = (tcolors[t]->b) << 8;
  1346.                     RGBForeColor(&hexcolor);
  1347.                     PaintPoly(poly);
  1348.                     /* Restore the previous color. */
  1349.                     oldcolor.red = oldcolor.green = oldcolor.blue = 0;
  1350.                     RGBForeColor(&oldcolor);
  1351.                 } else {
  1352.                     hexcolor.red   = 128 << 8;
  1353.                     hexcolor.green = 128 << 8;
  1354.                     hexcolor.blue  = 128 << 8;
  1355.                     RGBForeColor(&hexcolor);
  1356.                     PaintPoly(poly);
  1357.                     /* Restore the previous color. */
  1358.                     oldcolor.red = oldcolor.green = oldcolor.blue = 0;
  1359.                     RGBForeColor(&oldcolor);
  1360.                 }
  1361.                 KillPoly(poly);
  1362.             }
  1363.         }
  1364.     }
  1365. }
  1366.  
  1367. void
  1368. draw_borders(map, x, y, b)
  1369. Map *map;
  1370. int x, y, b;
  1371. {
  1372.     int xw = wrapx(x), dir, sx, sy, bitmask = 0;
  1373.     
  1374.      if (!terrain_visible(xw, y) || !any_borders_at(x, y, b)) return;
  1375.     for_all_directions(dir) {
  1376.         if (border_at(x, y, dir, b) && borders_visible(x, y, dir)) {
  1377.             bitmask |= 1 << dir;
  1378.         }
  1379.     }
  1380.     if (bitmask != 0) {
  1381.         xform(map, x, y, &sx, &sy);
  1382.         /* (should compute and pass in overlay) */
  1383.         draw_border_line_multiple(map->window, sx, sy, bitmask, map->vp->power, b, map->vp->angle);
  1384.     }
  1385. }
  1386.  
  1387. /* Draw all the connections of the given cell. */
  1388.  
  1389. void
  1390. draw_connections(map, x, y, c)
  1391. Map *map;
  1392. int x, y, c;
  1393. {
  1394.     int xw = wrapx(x), dir, sx, sy, bitmask = 0;
  1395.     
  1396.     if (!terrain_visible(xw, y) || !any_connections_at(x, y, c)) return;
  1397.     for_all_directions(dir) {
  1398.         if (connection_at(x, y, dir, c)) {
  1399.             bitmask |= 1 << dir;
  1400.         }
  1401.     }
  1402.     if (bitmask != 0) {
  1403.         xform(map, x, y, &sx, &sy);
  1404.         /* (should compute and pass in overlay) */
  1405.         draw_connection_line_multiple(map->window, sx, sy, bitmask, map->vp->power, c, map->vp->angle);
  1406.     }
  1407. }
  1408.  
  1409. /* Draw all the units visible in the given cell. */
  1410. /* (x is not wrapped) */
  1411.  
  1412. void
  1413. draw_units(map, x, y)
  1414. Map *map;
  1415. int x, y;
  1416. {
  1417.     int xw = wrapx(x), sx, sy, sw, sh, uview, u, s;
  1418.     Unit *unit;
  1419.     Rect tmprect;
  1420.     extern PicHandle dotdotdotpicture;
  1421.  
  1422.     if (units_visible(xw, y)) {
  1423.         unit = unit_at(xw, y);
  1424.         if (unit != NULL) {
  1425.             xform(map, x, y, &sx, &sy);
  1426.             if (map->vp->uw <= 16) {
  1427.                 /* Adjust to unit part of cell. */
  1428.                 sw = map->vp->uw;  sh = map->vp->uh;
  1429.                 sx += (map->vp->hw - sw) / 2;  sy += (map->vp->hh - sw) / 2;
  1430.                 if (unit->occupant != NULL
  1431.                     && (unit->side == dside || g_see_all() || u_see_occupants(unit->type))) {
  1432.                     /* Draw a "grouping box", in white, but with no occs drawn. */
  1433.                     SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
  1434.                     FillRect(&tmprect, QDPat(white));
  1435.                     FrameRect(&tmprect);
  1436.                 }
  1437.                 draw_unit_image(map->window, sx, sy, sw, sh,
  1438.                                 unit->type, side_number(unit->side), !completed(unit));
  1439.                 /* Indicate if more than one stacked here. */
  1440.                 if (unit->nexthere != NULL && sw > 8) {
  1441.                     SetRect(&tmprect, sx + sw/2 - 6, sy + sh - 2,
  1442.                                       sx + sw/2 + 6, sy + sh + 2);
  1443.                     /* (should clip to fit in cell) */
  1444.                     DrawPicture(dotdotdotpicture, &tmprect);
  1445.                 }
  1446.                 if (map->drawnames)
  1447.                   draw_unit_name(unit, sx, sy, sw, sh);
  1448.             } else {
  1449.                 for_all_stack(xw, y, unit) {
  1450.                     m_xform_unit(map, unit, &sx, &sy, &sw, &sh);
  1451.                     draw_unit_and_occs(map, unit, sx, sy, sw, sh);
  1452.                 }
  1453.             }
  1454.         }
  1455.     } else {
  1456.         uview = unit_view(dside, xw, y);
  1457.         if (uview != EMPTY) {
  1458.             u = vtype(uview);  s = vside(uview);
  1459.             xform(map, x, y, &sx, &sy);
  1460.             /* Adjust to unit part of cell. */
  1461.             sx += (map->vp->hw - map->vp->uw) / 2;  sy += (map->vp->hh - map->vp->uh) / 2;
  1462.             draw_unit_image(map->window, sx, sy, map->vp->uw, map->vp->uh, u, s, 0);
  1463.         }
  1464.     }
  1465. }
  1466.  
  1467. void
  1468. draw_unit_and_occs(map, unit, sx, sy, sw, sh)
  1469. Map *map;
  1470. Unit *unit;
  1471. int sx, sy, sw, sh;
  1472. {
  1473.     int u = unit->type, s = side_number(unit->side), sx2, sy2, sw2, sh2;
  1474.     Unit *occ;
  1475.     Rect tmprect;
  1476.  
  1477.     /* If an occupant's side is the same as its transport's, then there's
  1478.        really no need to draw its side emblem, since the transport's emblem
  1479.        will also be visible. */
  1480.     if (unit->transport && unit->side == unit->transport->side)
  1481.       s = -1;
  1482.     if (unit->occupant == NULL
  1483.         || (unit->side != dside && !g_see_all() && !u_see_occupants(unit->type))) {
  1484.         draw_unit_image(map->window, sx, sy, sw, sh, u, s, !completed(unit));
  1485.         if (map->drawnames)
  1486.           draw_unit_name(unit, sx, sy, sw, sh); 
  1487.     } else {
  1488.         /* Draw a sort of "grouping box", in white. */
  1489.         SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
  1490.         FillRect(&tmprect, QDPat(white));
  1491.         FrameRect(&tmprect);
  1492.         /* Draw the transport in the UL quarter of the box. */
  1493.         m_xform_occupant(map, unit, unit, sx, sy, sw, sh, &sx2, &sy2, &sw2, &sh2);
  1494.         draw_unit_image(map->window, sx2, sy2, sw2, sh2, u, s, !completed(unit));
  1495.         if (map->drawnames) draw_unit_name(unit, sx2, sy2, sw2, sh2);
  1496.         /* Draw all the occupants, in the bottom half of the box. */
  1497.         for_all_occupants(unit, occ) {
  1498.             m_xform_occupant(map, unit, occ, sx, sy, sw, sh, &sx2, &sy2, &sw2, &sh2);
  1499.             draw_unit_and_occs(map, occ, sx2, sy2, sw2, sh2);
  1500.         }
  1501.     }
  1502. }
  1503.  
  1504. /* Indicate what kind of people are living in the given row. */
  1505.  
  1506. /* (should optimize by sharing border drawing for max efficiency) */
  1507.  
  1508. void
  1509. draw_people_row(map, x0, y, len)
  1510. Map *map;
  1511. int x0, y, len;
  1512. {
  1513.     int pop, xx, x, sx, sy, sw, sh, ex, ey, ew, eh, dir, x1, y1, pop1;
  1514.     int bitmask1, bitmask2, drawemblemhere;
  1515.  
  1516.     for (xx = x0; xx < x0 + len; ++xx) {
  1517.         x = wrapx(xx);
  1518.         if (!terrain_visible(x, y)) continue;
  1519.         pop = people_side_at(x, y);
  1520.         bitmask1 = bitmask2 = 0;
  1521.         drawemblemhere = FALSE;
  1522.         /* Decide which edges are borders of the country. */
  1523.         for_all_directions(dir) {
  1524.             /* Don't do anything about edge cells. */
  1525.             if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
  1526.                 if (terrain_visible(x1, y1)) {
  1527.                     pop1 = people_side_at(x1, y1);
  1528.                     if (pop != pop1) {
  1529.                         /* Borders with uninhabitated regions are drawn differently. */
  1530.                         if (pop == NOBODY || pop1 == NOBODY) {
  1531.                             bitmask2 |= 1 << dir;
  1532.                         } else {
  1533.                             bitmask1 |= 1 << dir;
  1534.                         }
  1535.                     }
  1536.                 } else {
  1537.                     /* Draw just people in the cells right at the edge of the known world. */
  1538.                     drawemblemhere = TRUE;
  1539.                 }
  1540.             }
  1541.         }
  1542.         /* Now draw both the edges and an emblem for the cell. */
  1543.         if ((bitmask1 | bitmask2) != 0 || (map->drawpeople && drawemblemhere)) {
  1544.             xform(map, x, y, &sx, &sy);
  1545.             if (bitmask1 != 0) {
  1546.                 draw_country_borders(map->window, sx, sy, bitmask1, map->vp->power, 0, map->vp->angle);
  1547.             }
  1548.             if (bitmask2 != 0) {
  1549.                 draw_country_borders(map->window, sx, sy, bitmask2, map->vp->power, 2, map->vp->angle);
  1550.             }
  1551.             /* Draw an emblem for the people in the cell. */
  1552.             if (map->drawpeople && pop != NOBODY) {
  1553.                 sw = map->vp->uw;  sh = map->vp->uh;
  1554.                 ew = min(sw, max(8, sw / 2));  eh = min(sh, max(8, sh / 2));
  1555.                 ex = sx + (map->vp->hw - map->vp->uw) / 2 + sw / 2 - ew / 2;  ey = sy + (map->vp->hh - map->vp->uh) / 2 + sh / 2 - eh / 2;
  1556.                 draw_side_emblem(map->window, ex, ey, ew, eh, pop, plain_emblem);
  1557.             }
  1558.         }
  1559.     }
  1560. }
  1561.  
  1562. /* This draws a small set of bar charts, one for each material type. */
  1563.  
  1564. void
  1565. draw_materials(map, x, y)
  1566. Map *map;
  1567. int x, y;
  1568. {
  1569.     int m, t, sx, sy, mx, my, mw, mh, amt, maxamt, h;
  1570.     Rect graphrect;
  1571.     
  1572.     if (nummtypes == 0) return;
  1573.     mw = map->vp->uw / nummtypes /* should be count of displayable materials... */;  mh = map->vp->uh;
  1574.     if (mw <= 2 || mh <= 2) return;
  1575.     t = terrain_at(x, y);
  1576.     xform(map, x, y, &sx, &sy);
  1577.     mx = sx + (map->vp->hw - map->vp->uw) / 2;  my = sy + (map->vp->hh - map->vp->uh) / 2;
  1578.     for_all_material_types(m) {
  1579.         if (map->drawmaterials[m] && (maxamt = tm_storage_x(t, m)) > 0) {
  1580.             SetRect(&graphrect, mx + m * mw, my, mx + (m + 1) * mw, my + map->vp->uh);
  1581.             FrameRect(&graphrect);
  1582.             amt = material_at(x, y, m);
  1583.             h = (amt * mh) / maxamt;
  1584.             graphrect.top -= (mh - h);
  1585.             FillRect(&graphrect, QDPat(black));
  1586.         }
  1587.     }
  1588. }
  1589.  
  1590. void
  1591. draw_theater(map, x, y)
  1592. Map *map;
  1593. int x, y;
  1594. {
  1595.     int thid, sx, sy, dir, x1, y1, thid1, bitmask = 0;
  1596.  
  1597.     thid = ai_region_at(dside, wrapx(x), y);
  1598.     /* Decide which edges are borders of the theater. */
  1599.     for_all_directions(dir) {
  1600.         /* Don't do anything about edge cells. */
  1601.         if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
  1602.             thid1 = ai_region_at(dside, x1, y1);
  1603.             if (thid != thid1) {
  1604.                 bitmask |= 1 << dir;
  1605.             }
  1606.         }
  1607.     }
  1608.     if (bitmask != 0) {
  1609.         xform(map, x, y, &sx, &sy);
  1610.         if (bitmask != 0) {
  1611.             draw_theater_borders(map->window, sx, sy, bitmask, map->vp->power);
  1612.         }
  1613.     }
  1614. }
  1615.  
  1616. /* Draw any text that should be associated with this cell. */
  1617.  
  1618. /* (could precompute what the string will lap over and move or truncate str),
  1619.    should be deterministic for each mag, so redraw doesn't scramble */
  1620.  
  1621. void
  1622. draw_legend(map, x, y)
  1623. Map *map;
  1624. int x, y;
  1625. {
  1626.     int sx, sy;
  1627.     char *str, buf[BUFSIZE];
  1628.     Feature *feature;
  1629.  
  1630.     /* Draw the name of a terrain feature. */
  1631.     /* (should limit to one cell of feature, preferably centering on quasi-centroid) */    
  1632.     if (terrain_visible(wrapx(x), y)) {
  1633.         feature = feature_at(x, y);
  1634.         if (feature != NULL) {
  1635.             if (feature->size > 0) {
  1636.                 if ((feature->x == x && feature->y == y)
  1637.                     || (feature->x == 0 && feature->y == 0)
  1638.                     || 0 /* center far away */) {
  1639.                     str = feature_desc(feature, buf);
  1640.                     if (str != NULL) {
  1641.                         xform(map, x, y, &sx, &sy);
  1642.                         draw_legend_text(sx + map->vp->hw/2, sy + map->vp->hh/2, map->vp->uh, str, 0);
  1643.                     }
  1644.                 }
  1645.             }
  1646.         }
  1647.     }
  1648. }
  1649.  
  1650. /* Draw all the selections of all the units. */
  1651.  
  1652. void
  1653. draw_selections(map)
  1654. Map *map;
  1655. {
  1656.     int i;
  1657.     GrafPtr oldport;
  1658.     RgnHandle tmprgn;
  1659.     Unit *unit;
  1660.  
  1661.      GetPort(&oldport);
  1662.     SetPort(map->window);
  1663.     tmprgn = NewRgn();
  1664.     GetClip(tmprgn);
  1665.     ClipRect(&(map->contentrect));
  1666.     SHIFT_ORIGIN(map);
  1667.     for (i = 0; i < map->numselections; ++i) {
  1668.         unit = map->selections[i];
  1669.         draw_selected_unit(map, unit);
  1670.     }
  1671.     RESET_ORIGIN(map);
  1672.     SetClip(tmprgn);
  1673.     DisposeRgn(tmprgn);
  1674.     SetPort(oldport);
  1675. }
  1676.  
  1677. /* Draw all the selected units in the given cell. */
  1678.  
  1679. void
  1680. draw_selections_at(map, x, y)
  1681. Map *map;
  1682. int x, y;
  1683. {
  1684.     int i;
  1685.     GrafPtr oldport;
  1686.     RgnHandle tmprgn;
  1687.     Unit *unit;
  1688.  
  1689.      GetPort(&oldport);
  1690.     SetPort(map->window);
  1691.     tmprgn = NewRgn();
  1692.     GetClip(tmprgn);
  1693.     ClipRect(&(map->contentrect));
  1694.     SHIFT_ORIGIN(map);
  1695.     for (i = 0; i < map->numselections; ++i) {
  1696.         unit = map->selections[i];
  1697.         if (unit && unit->x == x && unit->y == y) {
  1698.             draw_selected_unit(map, unit);
  1699.         }
  1700.     }
  1701.     RESET_ORIGIN(map);
  1702.     SetClip(tmprgn);
  1703.     DisposeRgn(tmprgn);
  1704.     SetPort(oldport);
  1705. }
  1706.  
  1707. void
  1708. draw_selected_unit_setport(map, unit)
  1709. Map *map;
  1710. Unit *unit;
  1711. {
  1712.     GrafPtr oldport;
  1713.     RgnHandle tmprgn;
  1714.  
  1715.      GetPort(&oldport);
  1716.     SetPort(map->window);
  1717.     tmprgn = NewRgn();
  1718.     GetClip(tmprgn);
  1719.     ClipRect(&(map->contentrect));
  1720.     SHIFT_ORIGIN(map);
  1721.     draw_selected_unit(map, unit);
  1722.     RESET_ORIGIN(map);
  1723.     SetClip(tmprgn);
  1724.     DisposeRgn(tmprgn);
  1725.     SetPort(oldport);
  1726. }
  1727.  
  1728. /* Draw a single selected unit on the given map.  Assumes that grafport already set. */
  1729.  
  1730. void
  1731. draw_selected_unit(map, unit)
  1732. Map *map;
  1733. Unit *unit;
  1734. {
  1735.     int sx, sy, sw, sh, size, wholecell = FALSE, drawmag = FALSE;
  1736.     int sx1, sy1, sw1, sh1;
  1737.     Rect tmprect;
  1738.     extern Handle animationpatterns;
  1739.  
  1740.     if (!in_play(unit)) return; /* unselect it too? */
  1741.     if (map->vp->uw >= 32) {
  1742.         m_xform_unit_self(map, unit, &sx, &sy, &sw, &sh);
  1743.         if (map->numselections == 1
  1744.             && sw < 16
  1745.             && unit->transport != NULL) {
  1746.             wholecell = TRUE;
  1747.             drawmag = TRUE;
  1748.             sx1 = sx;  sy1 = sy;  sw1 = sw;  sh1 = sh;
  1749.         }
  1750.     } else {
  1751.         wholecell = TRUE;
  1752.     }
  1753.     if (wholecell) {
  1754.         xform(map, unit->x, unit->y, &sx, &sy);
  1755.         /* Adjust to unit part of cell. */
  1756.         sx += (map->vp->hw - map->vp->uw) / 2;  sy += (map->vp->hh - map->vp->uh) / 2;
  1757.         sw = map->vp->uw;  sh = map->vp->uh;
  1758.     }
  1759.     if (0 /* not actually within visible area */) return;
  1760.     /* Indicate a unit's plans/tasks in some useful fashion. */
  1761.     if (map->drawplans
  1762.         && unit->plan
  1763.         && unit->plan->tasks) {
  1764.         int sx2, sy2;
  1765.         Task *task = unit->plan->tasks, *nexttask;
  1766.  
  1767.         if (task != NULL) {
  1768.             if ((nexttask = task->next) != NULL) {
  1769.                 switch (nexttask->type) {
  1770.                     case TASK_MOVETO:
  1771.                     case TASK_HIT_UNIT:
  1772.                         if (in_area(nexttask->args[0], nexttask->args[1])) {
  1773.                             xform(map, nexttask->args[0], nexttask->args[1], &sx2, &sy2);
  1774.                             PenPat(QDPat(ltGray));
  1775.                             MoveTo(sx + sw/2, sy + sh/2);
  1776.                             LineTo(sx2 + map->vp->hw/2, sy2 + map->vp->hh/2);
  1777.                             PenNormal();
  1778.                         }
  1779.                         /* else warn unobtrusively? tasks should not go outside world,
  1780.                            but not a fatal problem if they do... */
  1781.                 }
  1782.             }
  1783.             switch (task->type) {
  1784.                 case TASK_MOVETO:
  1785.                 case TASK_HIT_UNIT:
  1786.                     if (in_area(task->args[0], task->args[1])) {
  1787.                         xform(map, task->args[0], task->args[1], &sx2, &sy2);
  1788.                         PenPat(QDPat(dkGray));
  1789.                         MoveTo(sx + sw/2, sy + sh/2);
  1790.                         LineTo(sx2 + map->vp->hw/2, sy2 + map->vp->hh/2);
  1791.                         PenNormal();
  1792.                     }
  1793.             }
  1794.         }
  1795.     }
  1796.     /* Draw magnification lines pointing to the true location of the unit. */
  1797.     if (drawmag) {
  1798.         /* PenPat should already be black. */
  1799.         MoveTo(sx,      sy);       LineTo(sx1,       sy1);
  1800.         MoveTo(sx + sw, sy);       LineTo(sx1 + sw1, sy1);
  1801.         MoveTo(sx,      sy + sh);  LineTo(sx1,       sy1 + sh1);
  1802.         MoveTo(sx + sw, sy + sh);  LineTo(sx1 + sw1, sy1 + sh1);
  1803.     }
  1804.     /* Be sure the selected unit is drawn. */
  1805.     draw_unit_image(map->window, sx, sy, sw, sh,
  1806.                     unit->type, side_number(unit->side), !completed(unit));
  1807.     /* Draw a highlighting rectangle. */
  1808.     SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
  1809.     /* A hack to prevent leakage into the grid. */
  1810.     if (map->drawgrid && map->vp->power == 5) --tmprect.bottom;
  1811.     /* First, draw an outer frame, for contrast. */
  1812.     if (map->autoselect && unit->act && unit->act->initacp > 0 && unit->act->acp > 0) {
  1813.         PenPat(((unsigned char *) (*animationpatterns)) + 8 * animation_pattern_state);
  1814.     } else {
  1815.         PenPat(QDPat(white));
  1816.     }
  1817.     FrameRect(&tmprect);
  1818.     InsetRect(&tmprect, 1, 1);
  1819.     /* Black is for units that can still act, dark gray for actors, gray if the
  1820.        unit can't do anything. */
  1821.     PenPat((unit->act && unit->act->initacp > 0) ?
  1822.             ((unit->act->acp > 0) ? QDPat(black) : QDPat(dkGray)) : QDPat(gray));
  1823.     /* Wide border if awake, narrow if asleep or napping. */
  1824.     size = ((unit->plan && (unit->plan->asleep || unit->plan->reserve)) ? 1 : 2);
  1825.     PenSize(size, size);
  1826.     FrameRect(&tmprect);
  1827.     PenNormal();
  1828.     DGprintf("draw selection of %s at %d,%d\n", unit_desig(unit));
  1829. }
  1830.  
  1831. void
  1832. draw_selection_animation(map, unit)
  1833. Map *map;
  1834. Unit *unit;
  1835. {
  1836.     int sx, sy, sw, sh, wholecell = FALSE, drawmag = FALSE;
  1837.     int sx1, sy1, sw1, sh1;
  1838.     Rect tmprect;
  1839.     extern Handle animationpatterns;
  1840.     GrafPtr oldport;
  1841.     RgnHandle tmprgn;
  1842.  
  1843.     if (!in_play(unit)) return; /* unselect it too? */
  1844.      GetPort(&oldport);
  1845.     SetPort(map->window);
  1846.     tmprgn = NewRgn();
  1847.     GetClip(tmprgn);
  1848.     ClipRect(&(map->contentrect));
  1849.     SHIFT_ORIGIN(map);
  1850.     if (map->vp->uw >= 32) {
  1851.         m_xform_unit_self(map, unit, &sx, &sy, &sw, &sh);
  1852.         if (map->numselections == 1
  1853.             && sw < 16
  1854.             && unit->transport != NULL) {
  1855.             wholecell = TRUE;
  1856.             drawmag = TRUE;
  1857.             sx1 = sx;  sy1 = sy;  sw1 = sw;  sh1 = sh;
  1858.         }
  1859.     } else {
  1860.         wholecell = TRUE;
  1861.     }
  1862.     if (wholecell) {
  1863.         xform(map, unit->x, unit->y, &sx, &sy);
  1864.         /* Adjust to unit part of cell. */
  1865.         sx += (map->vp->hw - map->vp->uw) / 2;  sy += (map->vp->hh - map->vp->uh) / 2;
  1866.         sw = map->vp->uw;  sh = map->vp->uh;
  1867.     }
  1868.     /* Draw a highlighting rectangle. */
  1869.     SetRect(&tmprect, sx, sy, sx + sw, sy + sh);
  1870.     /* A hack to prevent leakage into the grid. */
  1871.     if (map->drawgrid && map->vp->power == 5) --tmprect.bottom;
  1872.     /* First, draw an outer white frame, for contrast. */
  1873.     if (unit->act && unit->act->initacp > 0 && unit->act->acp > 0) {
  1874.         PenPat(((unsigned char *) (*animationpatterns)) + 8 * animation_pattern_state);
  1875.     } else {
  1876.         PenPat(QDPat(white));
  1877.     }
  1878.     FrameRect(&tmprect);
  1879.     PenPat(QDPat(black));
  1880.     RESET_ORIGIN(map);
  1881.     SetClip(tmprgn);
  1882.     DisposeRgn(tmprgn);
  1883.     SetPort(oldport);
  1884. }
  1885.  
  1886. /* (should only redraw any given cell once) */
  1887.  
  1888. void
  1889. erase_selections(map)
  1890. Map *map;
  1891. {
  1892.     int i;
  1893.     GrafPtr oldport;
  1894.     RgnHandle tmprgn;
  1895.     Unit *unit;
  1896.  
  1897.      GetPort(&oldport);
  1898.     SetPort(map->window);
  1899.     tmprgn = NewRgn();
  1900.     GetClip(tmprgn);
  1901.     ClipRect(&(map->contentrect));
  1902.     SHIFT_ORIGIN(map);
  1903.     for (i = 0; i < map->numselections; ++i) {
  1904.         unit = map->selections[i];
  1905.         draw_unselected_unit(map, unit);
  1906.     }
  1907.     RESET_ORIGIN(map);
  1908.     SetClip(tmprgn);
  1909.     DisposeRgn(tmprgn);
  1910.     SetPort(oldport);
  1911. }
  1912.  
  1913. void
  1914. erase_selection(map, unit)
  1915. Map *map;
  1916. Unit *unit;
  1917. {
  1918.     GrafPtr oldport;
  1919.     RgnHandle tmprgn;
  1920.  
  1921.      GetPort(&oldport);
  1922.     SetPort(map->window);
  1923.     tmprgn = NewRgn();
  1924.     GetClip(tmprgn);
  1925.     ClipRect(&(map->contentrect));
  1926.     SHIFT_ORIGIN(map);
  1927.     draw_unselected_unit(map, unit);
  1928.     RESET_ORIGIN(map);
  1929.     SetClip(tmprgn);
  1930.     DisposeRgn(tmprgn);
  1931.     SetPort(oldport);
  1932. }
  1933.  
  1934. void
  1935. draw_unselected_unit(map, unit)
  1936. Map *map;
  1937. Unit *unit;
  1938. {
  1939.     if (!in_play(unit)) return;
  1940.     draw_row(map, unit->x, unit->y, 1, TRUE);
  1941.     DGprintf("erase selection of %s at %d,%d\n", unit_desig(unit));
  1942. }
  1943.  
  1944. void
  1945. force_map_update(map)
  1946. Map *map;
  1947. {
  1948.     force_update(map->window);
  1949. }
  1950.  
  1951. /* Remove and destroy the map object. */
  1952.  
  1953. void
  1954. destroy_map(map)
  1955. Map *map;
  1956. {
  1957.     Map *map2;
  1958.     
  1959.     if (maplist == map) {
  1960.         maplist = map->next;
  1961.     } else {
  1962.         for_all_maps(map2) {
  1963.             if (map2->next == map) {
  1964.                 map2->next = map->next;
  1965.             }
  1966.         }
  1967.     }
  1968.     /* (should destroy substructs) */
  1969.     free(map);
  1970. }
  1971.  
  1972. void
  1973. activate_map(map, activate)
  1974. Map *map;
  1975. int activate;
  1976. {
  1977.     Rect growRect;
  1978.  
  1979.     if (activate) {
  1980.         HiliteControl(map->vscrollbar, 0);
  1981.         HiliteControl(map->hscrollbar, 0);
  1982.         /* Controls need to be redrawn on activation. */
  1983.         (*(map->vscrollbar))->contrlVis = 255;
  1984.         (*(map->hscrollbar))->contrlVis = 255;
  1985.         InvalRect(&(*(map->vscrollbar))->contrlRect);
  1986.         InvalRect(&(*(map->hscrollbar))->contrlRect);
  1987.         /* The growbox needs to be redrawn on activation. */
  1988.         growRect = map->window->portRect;
  1989.         /* adjust for the scrollbars */
  1990.         growRect.top = growRect.bottom - sbarwid;
  1991.         growRect.left = growRect.right - sbarwid;
  1992.         InvalRect(&growRect);
  1993.     } else {
  1994.         /* The scrollbars must be deactivated. */
  1995.         HiliteControl(map->vscrollbar, 255);
  1996.         HiliteControl(map->hscrollbar, 255);
  1997. #if 0  /* We don't want to hide them though, because the window bg is not white. */
  1998.         HideControl(map->vscrollbar);
  1999.         HideControl(map->hscrollbar);
  2000. #endif
  2001.         /* The growbox should be changed immediately on deactivation. */
  2002.         DrawGrowIcon(map->window);
  2003.     }
  2004. }
  2005.  
  2006. void
  2007. print_map(map)
  2008. Map *map;
  2009. {
  2010. /*    TPPrPort printport;
  2011.     extern THPrint printrecordhandle;
  2012.  
  2013.     printport = PrOpenDoc(printrecordhandle, nil, nil);
  2014.     PrCloseDoc(printport); */
  2015. }
  2016.  
  2017. /* (should be able to mention borders and conns also - share code with curses?) */
  2018.  
  2019. void
  2020. oneliner(map, sx, sy)
  2021. Map *map;
  2022. int sx, sy;
  2023. {
  2024.     int x, y, t2, uview, u, s, ps = NOBODY, dep, sayin = FALSE;
  2025.     char *peopdesc = NULL, *sidedesc;
  2026.     char descbuf[80];
  2027.     Unit *unit;
  2028.     Side *side;
  2029.     char *mplayer_at_desig();
  2030.  
  2031.     if (!m_nearest_cell(map, sx, sy, &x, &y)) {
  2032.         strcpy(tmpbuf, "(nothing)");
  2033.         return;
  2034.     } else if (terrain_visible(x, y)) {
  2035.         strcpy(tmpbuf, " ");
  2036.         /* Describe the side of the people here. */
  2037.         if (people_sides_defined()) {
  2038.             ps = people_side_at(x, y);
  2039.             if (ps != NOBODY) {
  2040.                 side = side_n(ps);
  2041.                 if (side == NULL) {
  2042.                     peopdesc = "indep";
  2043.                 } else if (side == dside) {
  2044.                     peopdesc = "your";
  2045.                 } else {
  2046.                     peopdesc = side_adjective(side);
  2047.                     if (peopdesc[0] == '\0') {
  2048.                         sprintf(descbuf, "s%d", side->id);
  2049.                         peopdesc = descbuf;
  2050.                     }
  2051.                 }
  2052.             }
  2053.         }
  2054.         if (units_visible(x, y)) {
  2055.             m_nearest_unit(map, sx, sy, &unit);
  2056.             if (unit != NULL) {
  2057.                 if (unit->side != dside) {
  2058.                     sidedesc = side_adjective(unit->side);
  2059.                     if (ps != NOBODY && ps == side_number(unit->side)) {
  2060.                         peopdesc = "own";
  2061.                     }
  2062.                 } else {
  2063.                     sidedesc = "your";
  2064.                 }
  2065.                 strcat(tmpbuf, sidedesc);
  2066.                 if (unit->name) {
  2067.                     strcat(tmpbuf, " ");
  2068.                     strcat(tmpbuf, u_type_name(unit->type));
  2069.                     strcat(tmpbuf, " ");
  2070.                     strcat(tmpbuf, unit->name);
  2071.                 } else if (unit->number > 0) {
  2072.                     tprintf(tmpbuf, " %d%s %s",
  2073.                             unit->number, ordinal_suffix(unit->number), u_type_name(unit->type));
  2074.                 } else {
  2075.                     strcat(tmpbuf, " ");
  2076.                     strcat(tmpbuf, u_type_name(unit->type));
  2077.                 }
  2078.                 if (Debug || DebugG || DebugM) {
  2079.                     tprintf(tmpbuf, " #%d", unit->id);
  2080.                 }
  2081.                 sayin = TRUE;
  2082.             }
  2083.         } else {
  2084.             if ((uview = unit_view(dside, x, y)) != EMPTY) {
  2085.                 u = vtype(uview);  s = vside(uview);
  2086.                 if (ps != NOBODY && ps == s) {
  2087.                     peopdesc = "own";
  2088.                 }
  2089.                 strcat(tmpbuf, side_adjective(side_n(s)));
  2090.                 strcat(tmpbuf, " ");
  2091.                 strcat(tmpbuf, u_type_name(u));
  2092.                 sayin = TRUE;
  2093.             }
  2094.         }
  2095.         if (sayin) {
  2096.             strcat(tmpbuf, " (in ");
  2097.         }
  2098.         if (peopdesc != NULL) {
  2099.             strcat(tmpbuf, peopdesc);
  2100.             strcat(tmpbuf, " ");
  2101.         }
  2102.         strcat(tmpbuf, t_type_name(terrain_at(x, y)));
  2103.         if (sayin) {
  2104.             strcat(tmpbuf, ")");
  2105.         }
  2106.         if (elevations_defined()) {
  2107.             tprintf(tmpbuf, " Elev %d", elev_at(x, y));
  2108.         }
  2109.         if (temperatures_defined()) {
  2110.             tprintf(tmpbuf, " T %d°", temperature_at(x, y));
  2111.         }
  2112.         if (numcoattypes > 0) {
  2113.             for_all_terrain_types(t2) {
  2114.                 if (t_is_coating(t2)
  2115.                     && aux_terrain_defined(t2)
  2116.                     && ((dep = aux_terrain_at(x, y, t2)) > 0)) {
  2117.                     tprintf(tmpbuf, " %s %d", t_type_name(t2), dep);
  2118.                 }
  2119.             }
  2120.         }
  2121.     } else {
  2122.         sprintf(tmpbuf, "(unknown)");
  2123.     }
  2124.     tprintf(tmpbuf, " @%d,%d", x, y);
  2125.     if (terrain_visible(x, y)) {
  2126.         Feature *feature;
  2127.         char *feature_desc();
  2128.         char *str, buf[BUFSIZE];
  2129.  
  2130.         feature = feature_at(x, y);
  2131.         if (feature != NULL) {
  2132.             if (feature->size > 0) {
  2133.                 str = feature_desc(feature, buf);
  2134.                 if (str != NULL) {
  2135.                     tprintf(tmpbuf, " (%s)", str);
  2136.                 }
  2137.             }
  2138.         }
  2139.     }
  2140.     if (map->drawai && side_has_ai(dside)) {
  2141.         strcat(tmpbuf, " ");
  2142.         strcat(tmpbuf, ai_at_desig(dside, x, y));
  2143.     }
  2144. }
  2145.